package network

import (
	"bytes"
	"encoding/binary"
	"io"
	"net"
	"sync"
	"time"

	"gomir/mlog"
	"gomir/network/proto/server"
)

// 当前客户端链接的服务阶段
type ServiceStage uint8

const (
	STAGE_NONE   ServiceStage = iota
	STAGE_LOGIN               // 登录阶段
	STAGE_SELECT              // 创建、选择角色阶段
	STAGE_GAME                // 登入游戏世界阶段
)

const (
	Msg_ClientAccepted = 1
	Msg_ClientClosed   = 2
)

type Message struct {
	Client *ClientConn
	MsgId  uint16
	Proto  interface{}
}

type MsgHandleCallback func(*Message)

var MsgHandler MsgHandleCallback = nil

func BindMsgHandler(callback MsgHandleCallback) {
	MsgHandler = callback
}

type ClientConn struct {
	conn       *net.TCPConn
	remoteAddr string

	Player   interface{}
	Stage    ServiceStage
	LastTime time.Time

	sendQueue chan Protocol // 发送队列

	sync.RWMutex
	isClosed bool
}

func NewClientConn(c *net.TCPConn) *ClientConn {
	client := &ClientConn{
		conn:       c,
		remoteAddr: c.RemoteAddr().String(),
		Player:     nil,
		Stage:      STAGE_NONE,
		sendQueue:  make(chan Protocol, 1024),
		LastTime:   time.Now(),
	}
	return client
}

func (client *ClientConn) IsClosed() bool {
	return client.isClosed
}

func (client *ClientConn) GetRemoteAddr() string {
	return client.remoteAddr
}

func (client *ClientConn) Post(p Protocol) {
	if !client.isClosed {
		client.sendQueue <- p
	}
}

func (client *ClientConn) Send(p interface{}) {
	if !client.isClosed {
		client.sendQueue <- p.(Protocol)
	}
}

func (client *ClientConn) Start() {
	go client.StartRecv()
	go client.StartSend()
	msg := &Message{
		Client: client,
		MsgId:  Msg_ClientAccepted,
		Proto:  nil,
	}
	MsgHandler(msg)
}
func (client *ClientConn) Stop() {
	client.Lock()
	defer client.Unlock()

	if client.isClosed {
		return
	}
	client.isClosed = true
	client.conn.Close()
	close(client.sendQueue)

	msg := &Message{
		Client: client,
		MsgId:  Msg_ClientClosed,
		Proto:  nil,
	}
	MsgHandler(msg)
}

func (client *ClientConn) StartRecv() {
	defer client.Stop()
	buf := make([]byte, 4)

	lastTime := time.Now()
	counter := 0

	for {
		if _, err := io.ReadFull(client.conn, buf); err != nil {
			mlog.Error(client.GetRemoteAddr(), "read1 error ", err)
			return
		}
		// 限制客户端发包速度
		counter++
		if counter > 16 {
			mlog.Error(client.GetRemoteAddr(), "too fast msg received:", counter)
			return
		}
		timeNow := time.Now()
		if timeNow.Sub(lastTime).Milliseconds() >= 1000 {
			counter = 0
			lastTime = timeNow
		}
		dataLen := binary.LittleEndian.Uint16(buf)

		//判断包最大长度
		if dataLen < 4 {
			mlog.Error(client.GetRemoteAddr(), "too short msg data received:", dataLen)
			return
		}
		if dataLen > 4096 {
			mlog.Error(client.GetRemoteAddr(), "too large msg data received:", dataLen)
			return
		}
		msgId := binary.LittleEndian.Uint16(buf[2:])
		msgId += 1000

		meta := ProtoMetaByID(int(msgId))
		if meta == nil {
			mlog.Errorf("%s Recv unknown msg=%d len=%d", client.conn.RemoteAddr(), msgId, dataLen)
			return
		}
		//根据 dataLen 读取 data
		var data []byte
		data = make([]byte, dataLen-4)
		if _, err := io.ReadFull(client.conn, data); err != nil {
			mlog.Error(client.GetRemoteAddr(), "read msg data error ", err)
			return
		}

		p := DecodeProtocol(int(msgId), data)
		if p == nil {
			mlog.Error(client.GetRemoteAddr(), "protocal decode err:", msgId)
			return
		}

		client.LastTime = time.Now()
		mlog.Debug(client.GetRemoteAddr(), "Recv", dataLen, meta.Name, p)

		msg := &Message{
			Client: client,
			MsgId:  msgId,
			Proto:  p,
		}
		MsgHandler(msg)
	}
}

func (client *ClientConn) StartSend() {
	defer client.Stop()
	buff := make([]byte, 4096)
	w := bytes.NewBuffer(buff)
	for {
		if client.isClosed {
			mlog.Warn(client.GetRemoteAddr(), "client connection is closed")
			return
		}

		p, ok := <-client.sendQueue
		if ok {
			meta := ProtoMetaByID(p.GetId())
			if meta == nil {
				mlog.Error(client.GetRemoteAddr(), "Send unknown type=", p.GetId())
				return
			}
			if p.GetId() == server.OBJECT_TURN ||
				p.GetId() == server.OBJECT_WALK ||
				p.GetId() == server.KEEP_ALIVE {

			} else {
				mlog.Debug(client.GetRemoteAddr(), "Send id=", meta.ID, meta.Name, p)
			}

			data := EncodeProtocol(p)
			if data == nil {
				mlog.Error("proto.EncodeProtocol nil", meta.Name, p)
				return
			}

			w.Reset()
			l := uint16(len(data)) + 4
			id := uint16(p.GetId()) - 2000
			binary.Write(w, binary.LittleEndian, &l)
			binary.Write(w, binary.LittleEndian, &id)
			if data != nil {
				binary.Write(w, binary.LittleEndian, data)
			}

			//数据写给客户端
			if _, err := client.conn.Write(w.Bytes()); err != nil {
				mlog.Error("Send Buff Data error:, ", err, " Conn Writer exit")
				return
			}
		} else {
			mlog.Error(client.GetRemoteAddr(), "client.sendQueue is Closed")
		}
	}
}
